搞了个遥控车,结果只有几十米的遥控距离,只能自己玩,这就很不爽。如果可以用手机控制,那么就可以让别的朋友,隔着几千里都能玩了。

说干就干,首先买个板子。

搞了个esp8266,可以输出pwm,并且有wifi模块,还支持python编程,这一下子就变成小孩子都能做的东西了。

还需要买一个电机驱动板,用来控制点击。

总体逻辑为:

1.通过驱动板来控制电机前进后退。

2.通过板子输出pwm来控制舵机的转动。

随便写几行代码。

from simple import MQTTClient
import network
import time
from machine import Pin, Timer, PWM

# 主要完成以下功能
# 1. 控制1个电机 控制命令[1:加速 2:减速 3:定速]
# 2. 实现踩下油门(加速)功能
# 3. 实现放下油门(减速)功能
# 4. 实现定速功能
# 5. 实现电机正反转 控制命令[4:正转 5:反转]
# 6. 实现转弯功能,具体表现为控制一个舵机,实现为pwm输出
# 7. 能够停止 控制命令[2]

# 控制命令
# 1加速 101减速 3定速 2停止 4左转 104左回正 5右转 105右回正 6倒车 7前进
# 默认是stop状态,开机需要先前进才可以

# 配置信息
MAX_SPEED = 1023    # 最大速度
MIN_SPEED = 150     # 最小速度
LEFT_ANGLE  = 45    # 左转的角度
RIGHT_ANGLE  = 135  # 左转的角度

# 无线网连接信息
SSID = "mxblog.mxguanwang.cn"
PASSWORD = "mx88888888"

# 服务器连接信息
SERVER = "mettserver"
CLIENT_ID = "clientid"
TOPIC = b"yourtopic"
username = 'name'
password = 'password'

# IO使用情况
# D1:控制电机速度 pwm
# D2,D3:控制电机正反转[1,0正转0,1反转0,0刹车1,1悬空]
# D4舵机 pwm 舵机逻辑为,pwm超过1.5ms正转2.5ms转一圈 小于1.5ms反转(1ms一圈0.5以下的被忽略)
# D5 被D4使用
# 这里假设左转是倒转,右转是正转
# P2 板子上的灯用来表示连接成功。1灯灭,未初始化完成,0灯亮,服务器连接成功
D1 = Pin(5, Pin.OUT, value=1)
D2 = Pin(4, Pin.OUT, value=1)
D3 = Pin(0, Pin.OUT, value=1)
D4 = Pin(14, Pin.OUT, value=1)
P2 = Pin(2, Pin.OUT, value=1)
sp_pwm = PWM(D1, 1000)              # 速度是持续性的,影响不大
fx_pwm = PWM(D4, 50)                # 舵机0.5ms-2.5ms其中0.5-1.5正转1.5-2.5反转
                                    # 使用500 则50%为1ms 100%为2ms实现正反转 转速53-62r/min
                                    # 所以占空比500/1000应该就是1秒1圈

# 状态信息
speed = 0       # 速度(0-1023)
state = 0       # 状态(初始0:加速:1 减速:2 定速:3)
lastspeed = 0   # 上次的速度,用来判断是否需要发送速度
sendidx = 0     # 每10次发一次
fx = 0          # 暂时修改为只能偏移到底不支持 微调[0:初始值中间1:向左2:向右]
back = False    # 是否是倒车
curangle = 90   # 当前的舵机角度

# 踩下油门(加速)
def OnAddSpeed():
    global state, speed
    if speed >= MAX_SPEED:
        state = 0
        speed = MAX_SPEED
        return
    state = 1

# 松开油门(减速)
def OnDecSpeed():
    global state, speed
    if speed <= MIN_SPEED:
        state = 0
        speed = MIN_SPEED
        return
    state = 2

def Revolve(angle):
    global curangle
    if fx == 0:
        curangle = 90
    elif fx == 1:
        curangle = LEFT_ANGLE
    elif fx == 2:
        curangle = RIGHT_ANGLE
    else:
        return
    # 如果目标角度大约当前角度则增加 否则则减少
    idx = 1
    if angle < curangle:
        idx = -1
    
    for i in range(curangle,angle,idx):
        fx_pwm.duty(int(125-i/180*100))
        time.sleep(0.0025)
    curangle = angle
    
    
# 左转
def OnLeft():
    global fx
    # 如果已经是左转了,那么就不处理
    if fx == 1:
        return
    Revolve(LEFT_ANGLE)
    fx = 1

# 右转
def OnRight():
    global fx
    # 如果已经是右转了,那么就不处理
    if fx == 2:
        return
    Revolve(RIGHT_ANGLE)
    fx = 2

# 中间
def OnCenter():
    global fx
    if fx == 0:
        return
    Revolve(90)
    fx = 0

# 前进
def OnFace():
    global back
    if back:
        OnNull()
    D2.value(1)
    D3.value(0)
    back = False

# 后退
def OnBack():
    global back
    if back:
        return
    else:
        OnNull()

    D2.value(0)
    D3.value(1)
    back = True

# 停止
def OnStop():
    OnNull()
    D2.value(0)
    D3.value(0)

# 悬空
def OnNull():
    global back
    back = False
    D2.value(1)
    D3.value(1)
    time.sleep(0.005)

# 接收消息处理
def OnMsg(topic, msg):
    # 过滤自己发送的消息
    if len(msg) > 1:
        stype = msg[0:2]
        if stype == b'sp':
            return
    global state
    print((topic, msg))
    cmd = msg
    if cmd == b'1':
        print("加速")
        OnAddSpeed()
        return

    if cmd == b'101':
        print("减速")
        OnDecSpeed()
        return

    if cmd == b'103':
        # 如果当前已经是定速,则取消定速,进入减速状态
        print("定速")
        if state == 3:
            state = 2
        else:
            state = 3
        return

    if cmd == b'2':
        print("停止")
        sp_pwm.duty(0)
        state = 0
        return

    if cmd == b'4':
        print("向左")
        OnLeft()
        return

    if cmd == b'104':
        print("左回正")
        OnCenter()
        return

    if cmd == b'5':
        print("向左")
        OnRight()
        return

    if cmd == b'105':
        print("右回正")
        OnCenter()
        return

    if cmd == b'6':
        print("倒车")
        OnBack()
        return

    if cmd == b'7':
        print("前进")
        OnFace()
        return
    return


# 连接无线网
def connectWifi(ssid, passwd):
    print("开始连接无线网")
    wlan = network.WLAN(network.STA_IF)  # create a wlan object
    # wlan.active(False)
    wlan.active(True)  # Activate the network interface
    wlan.disconnect()  # Disconnect the last connected WiFi
    wlan.connect(ssid, passwd)  # connect wifi
    while (wlan.ifconfig()[0] == '0.0.0.0'):
        time.sleep(1)
    print("无线网连接完成")


# 定时更新状态 4秒内速度加满 每秒进来100次所以每次增加1024/4/100
def UpdateState(id):
    global state, speed, sendidx, lastspeed
    if sendidx < 10:
        sendidx += 1
    else:
        sendidx = 0
        if lastspeed != speed:
            conn.publish(TOPIC, "sp:" + str(speed))
            lastspeed = speed

    if state == 1:
        speed += 1024 / 4 / 100
    elif state == 2:
        speed -= 1024 / 4 / 100
    else:
        return
    # 标准化数据
    if speed > MAX_SPEED:
        speed = MAX_SPEED
    elif speed < MIN_SPEED:
        speed = MIN_SPEED
    # 速度不变就跳过
    if int(speed) == sp_pwm.duty():
        return
    # print("\r当前状态" + str(state) + "当前速度" + str(speed))
    sp_pwm.duty(int(speed))

P2.value(1)
# 顺序:连接wifi -> 连接服务器 -> 订阅 -> 等待
wlan = connectWifi(SSID, PASSWORD)
conn = MQTTClient(CLIENT_ID, SERVER, 0, username, password)
conn.set_callback(OnMsg)
print("开始连接服务器")
conn.connect()
print("服务器连接完成\n开始订阅")
conn.subscribe(TOPIC)
print("订阅完成")
# 启动定时器,定时更新状态
tim = Timer(1)
tim.init(period=10, mode=Timer.PERIODIC, callback=UpdateState)
print("Connected to %s, subscribed to %s topic" % (SERVER, TOPIC))
sp_pwm.duty(MIN_SPEED)
OnStop()
P2.value(0)
while True:
    conn.wait_msg()

uPyCraft 烧录一下,soeasy

接线如下:

然后手机上搞个测试app,搞几个按钮,发送mqtt命令就可以控制了。

经过测试,延迟不大,基本上满足需求。

可以从代码中提取的东西:

  • 1.mqtt接收消息
  • 2.连接wifi
  • 3.输出pwm
  • 4.定时器
  • 5.控制舵机
  • 6.一种油门控制的实现方案

一个人活着,不是为了改变什么,而是为了在这个世界上留下点什么。如果某天,这篇文章帮到了你了,欢迎打赏,一分也是爱,并且以后如果实现了打赏墙,还可以上墙。


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。